#ifndef TERRAIN_HPP_
#define TERRAIN_HPP_

#include <list>

#include <device/aerDeviceBuffer.hpp>
#include <device/aerProgram.hpp>
#include <device/aerRenderTarget.hpp>
#include <view/aerCamera.hpp>
#include <misc/aerShape.hpp>


typedef enum {
  OT_EMPTY,
  OT_FILLED,
  OT_UNKNOWN
} ChunkTriState;

struct ChunkInfo
{
  int i, j, k;    // static
  float x, y, z;  // dynamic
  int id;
  //float distFromCamera;
  //ChunkTriState state;
};

#define GRID_DIM  7

class Terrain
{
  public:
    // Number of voxel per dimension for a chunk
    static const unsigned int kChunkDim = 32u;
    
    // Minimum size for a chunk in WorldSpace
    static const float kChunkSize = 1.0f;
    
    //kVoxelsPerSlice = kChunkDim*kChunkDim;
    //kVoxelSize = 1.0f / kChunkDim;
    //kDensityVolumeDim = kChunkDim + 1u;
    //kDensityVolumeTexelSize = 1.0f / kDensityVolumeDim;
    
    static const unsigned int kBufferBatchSize = 350;
    
    
  private:    
    GLuint m_queryTF;
    
  
    /// First Pass : Build density volume texture (for a chunk)
    aer::DeviceBuffer m_quadVB;
    aer::Program m_buildDensityPrgm;
    GLuint m_densityTex;
    GLuint m_densityRT;
    
    aer::Program m_dispDensityPrgm; // tmp for debug
    
    /// Second Pass : List triangle to generate per voxel
    struct
    {
      aer::Program program;
      aer::DeviceBuffer baseBuffer;
      aer::DeviceBuffer buffer;
      GLuint transformFeedback_id;
      GLuint tbo_caseToNumTri_id;
      GLuint tex_caseToNumTri_id;
      GLuint tbo_edgeConnect_id;
      GLuint tex_edgeConnect_id;
    } m_listTriangle;
    
    
    /// Third Pass : generate vertices from the triangle list
    aer::Program m_genVerticesPrgm;
    ChunkInfo m_chunkInfo[GRID_DIM][GRID_DIM][GRID_DIM];
    aer::DeviceBuffer m_chunkBuffer[kBufferBatchSize];
    GLuint m_chunkTF[kBufferBatchSize];
    aer::U32 m_freeBuffer[kBufferBatchSize];
    aer::U32 m_numFreeBuffer;
    
    
    /// Final Pass : render the cube chunk
    aer::Program m_renderChunkPrgm;
    aer::CubeRaw  m_cubeMesh;
    
  
  public:
    ~Terrain() {}//TODO
  
    void init();
    void createChunk(ChunkInfo &chunk);
    
    void initFirstPass();
    void buildDensityVolume(ChunkInfo &chunk);
    
    void initSecondPass();
    void listTriangles();
    
    void initThirdPass();
    void generateVertices(ChunkInfo &chunk);
    
    void initFinalPass();
    void render(aer::Camera *camera);
};

#endif //TERRAIN_HPP_
